////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any
//     purpose is hereby granted without fee, provided that the above copyright
//     notice appear in all copies and that both that copyright notice and this
//     permission notice appear in supporting documentation.
// The author or Addison-Wesley Longman make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
#ifndef LOKI_THREADS_INC_
#define LOKI_THREADS_INC_

// $Id: Threads.h 902 2008-11-10 05:47:06Z rich_sposato $


///  @defgroup  ThreadingGroup Threading
///  Policies to for the threading model:
///
///  - SingleThreaded
///  - ObjectLevelLockable
///  - ClassLevelLockable
///
///  All classes in Loki have configurable threading model.
///
///  The macro LOKI_DEFAULT_THREADING selects the default
///  threading model for certain components of Loki
///  (it affects only default template arguments)
///
///  \par Usage:
///
///  To use a specific threading model define
///
///  - nothing, single-theading is default
///  - LOKI_OBJECT_LEVEL_THREADING for object-level-threading
///  - LOKI_CLASS_LEVEL_THREADING for class-level-threading
///
///  \par Supported platfroms:
///
///  - Windows (windows.h)
///  - POSIX (pthread.h):
///    No recursive mutex support with pthread.
///    This means: calling Lock() on a Loki::Mutex twice from the
///    same thread before unlocking the mutex deadlocks the system.
///    To avoid this redesign your synchronization. See also:
///    http://sourceforge.net/tracker/index.php?func=detail&aid=1516182&group_id=29557&atid=396647


#include <cassert>

#if defined(LOKI_CLASS_LEVEL_THREADING) || defined(LOKI_OBJECT_LEVEL_THREADING)

#define LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL ::Loki::ClassLevelLockable

#if defined(LOKI_CLASS_LEVEL_THREADING) && !defined(LOKI_OBJECT_LEVEL_THREADING)
#define LOKI_DEFAULT_THREADING ::Loki::ClassLevelLockable
#else
#define LOKI_DEFAULT_THREADING ::Loki::ObjectLevelLockable
#endif

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define LOKI_WINDOWS_H
#else
#include <pthread.h>
#define LOKI_PTHREAD_H
#endif

#else

#define LOKI_DEFAULT_THREADING ::Loki::SingleThreaded
#define LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL ::Loki::SingleThreaded

#endif

#ifndef LOKI_DEFAULT_MUTEX
#define LOKI_DEFAULT_MUTEX ::Loki::Mutex
#endif

#ifdef LOKI_WINDOWS_H

#define LOKI_THREADS_MUTEX(x)           CRITICAL_SECTION (x);
#define LOKI_THREADS_MUTEX_INIT(x)      ::InitializeCriticalSection (x)
#define LOKI_THREADS_MUTEX_DELETE(x)    ::DeleteCriticalSection (x)
#define LOKI_THREADS_MUTEX_LOCK(x)      ::EnterCriticalSection (x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)    ::LeaveCriticalSection (x)
#define LOKI_THREADS_LONG               LONG
#define LOKI_THREADS_MUTEX_CTOR(x)

#define LOKI_THREADS_ATOMIC_FUNCTIONS                                   \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval *= val;                                                \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val)  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval /= val;                                                \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicIncrement(volatile IntType& lval)          \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            ++lval;                                                     \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDecrement(volatile IntType& lval)          \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            --lval;                                                     \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static void AtomicAssign(volatile IntType& lval, const IntType val)   \
        { InterlockedExchange(&const_cast<IntType&>(lval), val); }      \
                                                                        \
        static void AtomicAssign(IntType& lval, volatile const IntType& val)  \
        { InterlockedExchange(&lval, val); }                            \
                                                                        \
        static IntType AtomicIncrement(volatile IntType& lval, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            ++lval;                                                     \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDecrement(volatile IntType& lval, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            --lval;                                                     \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicAdd(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval += val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicSubtract(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval -= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval *= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )   \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                    \
            lval /= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                    \
            return lval;                                                \
        }

#elif defined(LOKI_PTHREAD_H)


#define LOKI_THREADS_MUTEX(x)           pthread_mutex_t (x);

#define LOKI_THREADS_MUTEX_INIT(x)      ::pthread_mutex_init(x, 0)

// define to 1 to enable recursive mutex support
#if 0
// experimental recursive mutex support
#define LOKI_THREADS_MUTEX_CTOR(x)      : x(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
#else
// no recursive mutex support
#define LOKI_THREADS_MUTEX_CTOR(x)
#endif

#define LOKI_THREADS_MUTEX_DELETE(x)    ::pthread_mutex_destroy (x)
#define LOKI_THREADS_MUTEX_LOCK(x)      ::pthread_mutex_lock (x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)    ::pthread_mutex_unlock (x)
#define LOKI_THREADS_LONG               long

#define LOKI_THREADS_ATOMIC(x)                                           \
                pthread_mutex_lock(&atomic_mutex_);                      \
                x;                                                       \
                pthread_mutex_unlock(&atomic_mutex_)

#define LOKI_THREADS_ATOMIC_FUNCTIONS                                \
    private:                                                         \
        static pthread_mutex_t atomic_mutex_;                        \
    public:                                                          \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval *= val;                                                 \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval /= val;                                                 \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicIncrement(volatile IntType& lval)           \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            ++lval;                                                      \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDecrement(volatile IntType& lval)           \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            --lval;                                                      \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static void AtomicAssign(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval = val;                                                  \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static void AtomicAssign(IntType& lval, volatile const IntType& val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval = val;                                                  \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicIncrement(volatile IntType& lval, const IntType compare, bool & matches ) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            ++lval;                                                      \
            matches = ( compare == lval );                               \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDecrement(volatile IntType& lval, const IntType compare, bool & matches ) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            --lval;                                                      \
            matches = ( compare == lval );                               \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::pthread_mutex_lock( &atomic_mutex_ );                     \
            lval *= val;                                                \
            matches = ( lval == compare );                              \
            ::pthread_mutex_unlock( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::pthread_mutex_lock( &atomic_mutex_ );                     \
            lval /= val;                                                \
            matches = ( lval == compare );                              \
            ::pthread_mutex_unlock( &atomic_mutex_ );                   \
            return lval;                                                \
        }

#else // single threaded

#define LOKI_THREADS_MUTEX(x)
#define LOKI_THREADS_MUTEX_INIT(x)
#define LOKI_THREADS_MUTEX_DELETE(x)
#define LOKI_THREADS_MUTEX_LOCK(x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)
#define LOKI_THREADS_LONG
#define LOKI_THREADS_MUTEX_CTOR(x)

#endif



namespace Loki
{

////////////////////////////////////////////////////////////////////////////////
///  \class Mutex
//
///  \ingroup ThreadingGroup
///  A simple and portable Mutex.  A default policy class for locking objects.
////////////////////////////////////////////////////////////////////////////////

class Mutex
{
public:
	Mutex() LOKI_THREADS_MUTEX_CTOR(mtx_)
	{
		LOKI_THREADS_MUTEX_INIT(&mtx_);
	}
	~Mutex()
	{
		LOKI_THREADS_MUTEX_DELETE(&mtx_);
	}
	void Lock()
	{
		LOKI_THREADS_MUTEX_LOCK(&mtx_);
	}
	void Unlock()
	{
		LOKI_THREADS_MUTEX_UNLOCK(&mtx_);
	}
private:
	/// Copy-constructor not implemented.
	Mutex(const Mutex &);
	/// Copy-assignement operator not implemented.
	Mutex &operator = (const Mutex &);
	LOKI_THREADS_MUTEX(mtx_)
};


////////////////////////////////////////////////////////////////////////////////
///  \class SingleThreaded
///
///  \ingroup ThreadingGroup
///  Implementation of the ThreadingModel policy used by various classes
///  Implements a single-threaded model; no synchronization
////////////////////////////////////////////////////////////////////////////////
template <class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX>
class SingleThreaded
{
public:
	/// \struct Lock
	/// Dummy Lock class
	struct Lock
	{
		Lock() {}
		explicit Lock(const SingleThreaded &) {}
		explicit Lock(const SingleThreaded *) {}
	};

	typedef Host VolatileType;

	typedef int IntType;

	static IntType AtomicAdd(volatile IntType &lval, const IntType val)
	{
		return lval += val;
	}

	static IntType AtomicSubtract(volatile IntType &lval, const IntType val)
	{
		return lval -= val;
	}

	static IntType AtomicMultiply(volatile IntType &lval, const IntType val)
	{
		return lval *= val;
	}

	static IntType AtomicDivide(volatile IntType &lval, const IntType val)
	{
		return lval /= val;
	}

	static IntType AtomicIncrement(volatile IntType &lval)
	{
		return ++lval;
	}

	static IntType AtomicDecrement(volatile IntType &lval)
	{
		return --lval;
	}

	static void AtomicAssign(volatile IntType &lval, const IntType val)
	{
		lval = val;
	}

	static void AtomicAssign(IntType &lval, volatile IntType &val)
	{
		lval = val;
	}

	static IntType AtomicAdd(volatile IntType &lval, const IntType val, const IntType compare, bool &matches )
	{
		lval += val;
		matches = ( lval == compare );
		return lval;
	}

	static IntType AtomicSubtract(volatile IntType &lval, const IntType val, const IntType compare, bool &matches )
	{
		lval -= val;
		matches = ( lval == compare );
		return lval;
	}

	static IntType AtomicMultiply(volatile IntType &lval, const IntType val, const IntType compare, bool &matches )
	{
		lval *= val;
		matches = ( lval == compare );
		return lval;
	}

	static IntType AtomicDivide(volatile IntType &lval, const IntType val, const IntType compare, bool &matches )
	{
		lval /= val;
		matches = ( lval == compare );
		return lval;
	}

	static IntType AtomicIncrement(volatile IntType &lval, const IntType compare, bool &matches )
	{
		++lval;
		matches = ( lval == compare );
		return lval;
	}

	static IntType AtomicDecrement(volatile IntType &lval, const IntType compare, bool &matches )
	{
		--lval;
		matches = ( lval == compare );
		return lval;
	}

};


#if defined(LOKI_WINDOWS_H) || defined(LOKI_PTHREAD_H)

////////////////////////////////////////////////////////////////////////////////
///  \class ObjectLevelLockable
///
///  \ingroup ThreadingGroup
///  Implementation of the ThreadingModel policy used by various classes
///  Implements a object-level locking scheme
////////////////////////////////////////////////////////////////////////////////
template < class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX >
class ObjectLevelLockable
{
	mutable MutexPolicy mtx_;

public:
	ObjectLevelLockable() : mtx_() {}

	ObjectLevelLockable(const ObjectLevelLockable &) : mtx_() {}

	~ObjectLevelLockable() {}

	class Lock;
	friend class Lock;

	///  \struct Lock
	///  Lock class to lock on object level
	class Lock
	{
	public:

		/// Lock object
		explicit Lock(const ObjectLevelLockable &host) : host_(host)
		{
			host_.mtx_.Lock();
		}

		/// Lock object
		explicit Lock(const ObjectLevelLockable *host) : host_(*host)
		{
			host_.mtx_.Lock();
		}

		/// Unlock object
		~Lock()
		{
			host_.mtx_.Unlock();
		}

	private:
		/// private by design of the object level threading
		Lock();
		Lock(const Lock &);
		Lock &operator=(const Lock &);
		const ObjectLevelLockable &host_;
	};

	typedef volatile Host VolatileType;

	typedef LOKI_THREADS_LONG IntType;

	LOKI_THREADS_ATOMIC_FUNCTIONS

};

#ifdef LOKI_PTHREAD_H
template <class Host, class MutexPolicy>
pthread_mutex_t ObjectLevelLockable<Host, MutexPolicy>::atomic_mutex_ = PTHREAD_MUTEX_INITIALIZER;
#endif

////////////////////////////////////////////////////////////////////////////////
///  \class ClassLevelLockable
///
///  \ingroup ThreadingGroup
///  Implementation of the ThreadingModel policy used by various classes
///  Implements a class-level locking scheme
////////////////////////////////////////////////////////////////////////////////
template <class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX >
class ClassLevelLockable
{
	struct Initializer
	{
		bool init_;
		MutexPolicy mtx_;

		Initializer() : init_(false), mtx_()
		{
			init_ = true;
		}

		~Initializer()
		{
			assert(init_);
		}
	};

	static Initializer initializer_;

public:

	class Lock;
	friend class Lock;

	///  \struct Lock
	///  Lock class to lock on class level
	class Lock
	{
	public:

		/// Lock class
		Lock()
		{
			assert(initializer_.init_);
			initializer_.mtx_.Lock();
		}

		/// Lock class
		explicit Lock(const ClassLevelLockable &)
		{
			assert(initializer_.init_);
			initializer_.mtx_.Lock();
		}

		/// Lock class
		explicit Lock(const ClassLevelLockable *)
		{
			assert(initializer_.init_);
			initializer_.mtx_.Lock();
		}

		/// Unlock class
		~Lock()
		{
			assert(initializer_.init_);
			initializer_.mtx_.Unlock();
		}

	private:
		Lock(const Lock &);
		Lock &operator=(const Lock &);
	};

	typedef volatile Host VolatileType;

	typedef LOKI_THREADS_LONG IntType;

	LOKI_THREADS_ATOMIC_FUNCTIONS

};

#ifdef LOKI_PTHREAD_H
template <class Host, class MutexPolicy>
pthread_mutex_t ClassLevelLockable<Host, MutexPolicy>::atomic_mutex_ = PTHREAD_MUTEX_INITIALIZER;
#endif

template < class Host, class MutexPolicy >
typename ClassLevelLockable< Host, MutexPolicy >::Initializer
ClassLevelLockable< Host, MutexPolicy >::initializer_;

#endif // #if defined(LOKI_WINDOWS_H) || defined(LOKI_PTHREAD_H)

} // namespace Loki


#endif // end file guardian

